﻿using System.Linq;
using Borg.AddOn;
using System;
using System.Collections.Generic;
using System.Reflection;

namespace Borg
{
    [Serializable]
    public class Notifier : RuntimeElement
    {
        private int _onDoneHandlerExcutionId;

        private readonly Stack<IActionInvoker> _onCompositionDoneStack;
        private readonly Action _executeOnDoneHandlers;

        private bool _isExecutingCanHandler;
        private bool _isOnDoneHandlerExecutionInvoked;

        private EventHandlerWrapper<ContractEventArgs>[] _typeStoreContractAddedEventHandlers;
        private EventHandlerWrapper<ContractEventArgs>[] _typeStoreContractRemovedEventHandlers;

        private EventHandlerWrapper<PluginEventArgs>[] _typeStorePluginAddedEventHandlers;
        private EventHandlerWrapper<PluginEventArgs>[] _typeStorePluginRemovedEventHandlers;

        private EventHandlerWrapper<EventArgs>[] _composerCompositionDoneEventHandlers;

        private EventHandlerWrapper<ExtensionEventArgs>[] _composerExtensionCreatedEventHandlers;
        private EventHandlerWrapper<ExtensionEventArgs>[] _composerExtensionDestroyedEventHandlers;

        private EventHandlerWrapper<ExtensionEventArgs>[] _composerExtensionActivatedEventHandlers;
        private EventHandlerWrapper<ExtensionEventArgs>[] _composerExtensionDeactivatedEventHandlers;

        private EventHandlerWrapper<SlotEventArgs>[] _composerSlotOpenedEventHandlers;
        private EventHandlerWrapper<SlotEventArgs>[] _composerSlotClosedEventHandlers;

        private EventHandlerWrapper<CompositionEventArgs>[] _composerExtensionPluggedEventHandlers;
        private EventHandlerWrapper<CompositionEventArgs>[] _composerExtensionUnpluggedEventHandlers;

        private EventHandlerWrapper<TagEventArgs>[] _composerExtensionTaggedEventHandlers;
        private EventHandlerWrapper<TagEventArgs>[] _composerExtensionUntaggedEventHandlers;

        private readonly Dictionary<ExtensionType, EventHandlerWrapper<ExtensionEventArgs>[]> _extensionTypeCreatedEventHandlers;
        private readonly Dictionary<ExtensionType, EventHandlerWrapper<ExtensionEventArgs>[]> _extensionTypeDestroyedEventHandlers;
        
        private readonly Dictionary<Extension, EventHandlerWrapper<ExtensionEventArgs>[]> _extensionActivatedEventHandlers;
        private readonly Dictionary<Extension, EventHandlerWrapper<ExtensionEventArgs>[]> _extensionDeactivatedEventHandlers;
        
        private readonly Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]> _plugPluggingEventHandlers;
        private readonly Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]> _plugPluggedEventHandlers;
        
        private readonly Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]> _plugUnpluggingEventHandlers;
        private readonly Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]> _plugUnpluggedEventHandlers;
        
        private readonly Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]> _plugTaggingEventHandlers;
        private readonly Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]> _plugTaggedEventHandlers;
        
        private readonly Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]> _plugUntaggingEventHandlers;
        private readonly Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]> _plugUntaggedEventHandlers;
        
        private readonly Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]> _slotOpeningEventHandlers;
        private readonly Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]> _slotOpenedEventHandlers;
        
        private readonly Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]> _slotClosingEventHandlers;
        private readonly Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]> _slotClosedEventHandlers;
        
        private readonly Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]> _slotPluggingEventHandlers;
        private readonly Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]> _slotPluggedEventHandlers;
        
        private readonly Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]> _slotUnpluggingEventHandlers;
        private readonly Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]> _slotUnpluggedEventHandlers;
        
        private readonly Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]> _slotTaggingEventHandlers;
        private readonly Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]> _slotTaggedEventHandlers;
        
        private readonly Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]> _slotUntaggingEventHandlers;
        private readonly Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]> _slotUntaggedEventHandlers;

        private readonly Dictionary<Slot, BehaviorCollection> _slotBehaviors;
        private BehaviorCollection _composerBehaviors;

        private ICompositionPermissionHook _permissionHook;
        private INotifierNotificationHook _notificationHook;

        internal bool IsExecutingCanHandler
        {
            get { return _isExecutingCanHandler; }
            private set { _isExecutingCanHandler = value; }
        }

        protected internal Notifier(Runtime runtime)
            : base(runtime)
        {
            _onCompositionDoneStack = new Stack<IActionInvoker>();
            _executeOnDoneHandlers = ExecuteOnDoneHandlers;

            _extensionTypeCreatedEventHandlers = new Dictionary<ExtensionType, EventHandlerWrapper<ExtensionEventArgs>[]>();
            _extensionTypeDestroyedEventHandlers = new Dictionary<ExtensionType, EventHandlerWrapper<ExtensionEventArgs>[]>();
            
            _extensionActivatedEventHandlers = new Dictionary<Extension, EventHandlerWrapper<ExtensionEventArgs>[]>();
            _extensionDeactivatedEventHandlers = new Dictionary<Extension, EventHandlerWrapper<ExtensionEventArgs>[]>();
            
            _plugPluggingEventHandlers = new Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]>();
            _plugPluggedEventHandlers = new Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]>();
            
            _plugUnpluggingEventHandlers = new Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]>();
            _plugUnpluggedEventHandlers = new Dictionary<Plug, EventHandlerWrapper<CompositionEventArgs>[]>();
            
            _plugTaggingEventHandlers = new Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]>();
            _plugTaggedEventHandlers = new Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]>();
            
            _plugUntaggingEventHandlers = new Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]>();
            _plugUntaggedEventHandlers = new Dictionary<Plug, EventHandlerWrapper<TagEventArgs>[]>();
            
            _slotOpeningEventHandlers = new Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]>();
            _slotOpenedEventHandlers = new Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]>();
            
            _slotClosingEventHandlers = new Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]>();
            _slotClosedEventHandlers = new Dictionary<Slot, EventHandlerWrapper<SlotEventArgs>[]>();
            
            _slotPluggingEventHandlers = new Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]>();
            _slotPluggedEventHandlers = new Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]>();
            
            _slotUnpluggingEventHandlers = new Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]>();
            _slotUnpluggedEventHandlers = new Dictionary<Slot, EventHandlerWrapper<CompositionEventArgs>[]>();
            
            _slotTaggingEventHandlers = new Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]>();
            _slotTaggedEventHandlers = new Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]>();
            
            _slotUntaggingEventHandlers = new Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]>();
            _slotUntaggedEventHandlers = new Dictionary<Slot, EventHandlerWrapper<TagEventArgs>[]>();
            
            _slotBehaviors = new Dictionary<Slot, BehaviorCollection>();
        }

        internal void Init(ICompositionPermissionHook permissionHook, INotifierNotificationHook notificationHook)
        {
            _permissionHook = permissionHook;
            _notificationHook = notificationHook;
            
            _composerBehaviors = new ComposerBehaviorCollection(notificationHook, Composer);
        }

        internal void AddContractAddedHandler(EventHandler<ContractEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStoreContractAddedEventHandlers =
                AddHandler(_typeStoreContractAddedEventHandlers, eventHandler);
        }

        internal void RemoveContractAddedHandler(EventHandler<ContractEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStoreContractAddedEventHandlers =
                RemoveHandler(_typeStoreContractAddedEventHandlers, eventHandler);
        }

        internal void AddContractRemovedHandler(EventHandler<ContractEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStoreContractRemovedEventHandlers =
                AddHandler(_typeStoreContractRemovedEventHandlers, eventHandler);
        }

        internal void RemoveContractRemovedHandler(EventHandler<ContractEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStoreContractRemovedEventHandlers =
                RemoveHandler(_typeStoreContractRemovedEventHandlers, eventHandler);
        }

        internal void AddPluginAddedHandler(EventHandler<PluginEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStorePluginAddedEventHandlers =
                AddHandler(_typeStorePluginAddedEventHandlers, eventHandler);
        }

        internal void RemovePluginAddedHandler(EventHandler<PluginEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStorePluginAddedEventHandlers =
                RemoveHandler(_typeStorePluginAddedEventHandlers, eventHandler);
        }

        internal void AddPluginRemovedHandler(EventHandler<PluginEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStorePluginRemovedEventHandlers =
                AddHandler(_typeStorePluginRemovedEventHandlers, eventHandler);
        }

        internal void RemovePluginRemovedHandler(EventHandler<PluginEventArgs> eventHandler)
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            _typeStorePluginRemovedEventHandlers =
                RemoveHandler(_typeStorePluginRemovedEventHandlers, eventHandler);
        }

        internal void AddCompositionDoneHandler(EventHandler<EventArgs> eventHandler)
        {
            _composerCompositionDoneEventHandlers =
                AddHandler(_composerCompositionDoneEventHandlers, eventHandler);
        }

        internal void RemoveCompositionDoneHandler(EventHandler<EventArgs> eventHandler)
        {
            _composerCompositionDoneEventHandlers =
                RemoveHandler(_composerCompositionDoneEventHandlers, eventHandler);
        }

        internal void AddExtensionCreatedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionCreatedEventHandlers =
                AddHandler(_composerExtensionCreatedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionCreatedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionCreatedEventHandlers =
                RemoveHandler(_composerExtensionCreatedEventHandlers, eventHandler);
        }

        internal void AddExtensionDestroyedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionDestroyedEventHandlers =
                AddHandler(_composerExtensionDestroyedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionDestroyedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionDestroyedEventHandlers =
                RemoveHandler(_composerExtensionDestroyedEventHandlers, eventHandler);
        }

        internal void AddExtensionActivatedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionActivatedEventHandlers =
                AddHandler(_composerExtensionActivatedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionActivatedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionActivatedEventHandlers =
                RemoveHandler(_composerExtensionActivatedEventHandlers, eventHandler);
        }

        internal void AddExtensionDeactivatedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionDeactivatedEventHandlers =
                AddHandler(_composerExtensionDeactivatedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionDeactivatedHandler(EventHandler<ExtensionEventArgs> eventHandler)
        {
            _composerExtensionDeactivatedEventHandlers =
                RemoveHandler(_composerExtensionDeactivatedEventHandlers, eventHandler);
        }

        internal void AddSlotOpenedHandler(EventHandler<SlotEventArgs> eventHandler)
        {
            _composerSlotOpenedEventHandlers =
                AddHandler(_composerSlotOpenedEventHandlers, eventHandler);
        }

        internal void RemoveSlotOpenedHandler(EventHandler<SlotEventArgs> eventHandler)
        {
            _composerSlotOpenedEventHandlers =
                RemoveHandler(_composerSlotOpenedEventHandlers, eventHandler);
        }

        internal void AddSlotClosedHandler(EventHandler<SlotEventArgs> eventHandler)
        {
            _composerSlotClosedEventHandlers =
                AddHandler(_composerSlotClosedEventHandlers, eventHandler);
        }

        internal void RemoveSlotClosedHandler(EventHandler<SlotEventArgs> eventHandler)
        {
            _composerSlotClosedEventHandlers =
                RemoveHandler(_composerSlotClosedEventHandlers, eventHandler);
        }

        internal void AddExtensionPluggedHandler(EventHandler<CompositionEventArgs> eventHandler)
        {
            _composerExtensionPluggedEventHandlers =
                AddHandler(_composerExtensionPluggedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionPluggedHandler(EventHandler<CompositionEventArgs> eventHandler)
        {
            _composerExtensionPluggedEventHandlers =
                RemoveHandler(_composerExtensionPluggedEventHandlers, eventHandler);
        }

        internal void AddExtensionUnpluggedHandler(EventHandler<CompositionEventArgs> eventHandler)
        {
            _composerExtensionUnpluggedEventHandlers =
                AddHandler(_composerExtensionUnpluggedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionUnpluggedHandler(EventHandler<CompositionEventArgs> eventHandler)
        {
            _composerExtensionUnpluggedEventHandlers =
                RemoveHandler(_composerExtensionUnpluggedEventHandlers, eventHandler);
        }

        internal void AddExtensionTaggedHandler(EventHandler<TagEventArgs> eventHandler)
        {
            _composerExtensionTaggedEventHandlers =
                AddHandler(_composerExtensionTaggedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionTaggedHandler(EventHandler<TagEventArgs> eventHandler)
        {
            _composerExtensionTaggedEventHandlers =
                RemoveHandler(_composerExtensionTaggedEventHandlers, eventHandler);
        }

        internal void AddExtensionUntaggedHandler(EventHandler<TagEventArgs> eventHandler)
        {
            _composerExtensionUntaggedEventHandlers =
                AddHandler(_composerExtensionUntaggedEventHandlers, eventHandler);
        }

        internal void RemoveExtensionUntaggedHandler(EventHandler<TagEventArgs> eventHandler)
        {
            _composerExtensionUntaggedEventHandlers =
                RemoveHandler(_composerExtensionUntaggedEventHandlers, eventHandler);
        }

        internal void AddCreatedHandler(ExtensionType extensionType, EventHandler<ExtensionEventArgs> eventHandler)
        {
            AddHandler(_extensionTypeCreatedEventHandlers, extensionType, eventHandler);
        }

        internal void RemoveCreatedHandler(ExtensionType extensionType, EventHandler<ExtensionEventArgs> eventHandler)
        {
            RemoveHandler(_extensionTypeCreatedEventHandlers, extensionType, eventHandler);
        }

        internal void AddDestroyedHandler(ExtensionType extensionType, EventHandler<ExtensionEventArgs> eventHandler)
        {
            AddHandler(_extensionTypeDestroyedEventHandlers, extensionType, eventHandler);
        }

        internal void RemoveDestroyedHandler(ExtensionType extensionType, EventHandler<ExtensionEventArgs> eventHandler)
        {
            RemoveHandler(_extensionTypeDestroyedEventHandlers, extensionType, eventHandler);
        }

        internal void AddActivatedHandler(Extension extension, EventHandler<ExtensionEventArgs> eventHandler)
        {
            AddHandler(_extensionActivatedEventHandlers, extension, eventHandler);
        }

        internal void RemoveActivatedHandler(Extension extension, EventHandler<ExtensionEventArgs> eventHandler)
        {
            RemoveHandler(_extensionActivatedEventHandlers, extension, eventHandler);
        }

        internal void AddDeactivatedHandler(Extension extension, EventHandler<ExtensionEventArgs> eventHandler)
        {
            AddHandler(_extensionDeactivatedEventHandlers, extension, eventHandler);
        }

        internal void RemoveDeactivatedHandler(Extension extension, EventHandler<ExtensionEventArgs> eventHandler)
        {
            RemoveHandler(_extensionDeactivatedEventHandlers, extension, eventHandler);
        }

        internal void AddPluggingHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_plugPluggingEventHandlers, plug, eventHandler);
        }

        internal void RemovePluggingHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_plugPluggingEventHandlers, plug, eventHandler);
        }

        internal void AddPluggedHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_plugPluggedEventHandlers, plug, eventHandler);
        }

        internal void RemovePluggedHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_plugPluggedEventHandlers, plug, eventHandler);
        }

        internal void AddUnpluggingHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_plugUnpluggingEventHandlers, plug, eventHandler);
        }

        internal void RemoveUnpluggingHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_plugUnpluggingEventHandlers, plug, eventHandler);
        }

        internal void AddUnpluggedHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_plugUnpluggedEventHandlers, plug, eventHandler);
        }

        internal void RemoveUnpluggedHandler(Plug plug, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_plugUnpluggedEventHandlers, plug, eventHandler);
        }

        internal void AddTaggingHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_plugTaggingEventHandlers, plug, eventHandler);
        }

        internal void RemoveTaggingHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_plugTaggingEventHandlers, plug, eventHandler);
        }

        internal void AddTaggedHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_plugTaggedEventHandlers, plug, eventHandler);
        }

        internal void RemoveTaggedHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_plugTaggedEventHandlers, plug, eventHandler);
        }

        internal void AddUntaggingHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_plugUntaggingEventHandlers, plug, eventHandler);
        }

        internal void RemoveUntaggingHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_plugUntaggingEventHandlers, plug, eventHandler);
        }

        internal void AddUntaggedHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_plugUntaggedEventHandlers, plug, eventHandler);
        }

        internal void RemoveUntaggedHandler(Plug plug, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_plugUntaggedEventHandlers, plug, eventHandler);
        }

        internal void AddOpeningHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            AddHandler(_slotOpeningEventHandlers, slot, eventHandler);
        }

        internal void RemoveOpeningHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            RemoveHandler(_slotOpeningEventHandlers, slot, eventHandler);
        }

        internal void AddOpenedHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            AddHandler(_slotOpenedEventHandlers, slot, eventHandler);
        }

        internal void RemoveOpenedHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            RemoveHandler(_slotOpenedEventHandlers, slot, eventHandler);
        }

        internal void AddClosingHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            AddHandler(_slotClosingEventHandlers, slot, eventHandler);
        }

        internal void RemoveClosingHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            RemoveHandler(_slotClosingEventHandlers, slot, eventHandler);
        }

        internal void AddClosedHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            AddHandler(_slotClosedEventHandlers, slot, eventHandler);
        }

        internal void RemoveClosedHandler(Slot slot, EventHandler<SlotEventArgs> eventHandler)
        {
            RemoveHandler(_slotClosedEventHandlers, slot, eventHandler);
        }

        internal void AddPluggingHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_slotPluggingEventHandlers, slot, eventHandler);
        }

        internal void RemovePluggingHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_slotPluggingEventHandlers, slot, eventHandler);
        }

        internal void AddPluggedHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_slotPluggedEventHandlers, slot, eventHandler);
        }

        internal void RemovePluggedHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_slotPluggedEventHandlers, slot, eventHandler);
        }

        internal void AddUnpluggingHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_slotUnpluggingEventHandlers, slot, eventHandler);
        }

        internal void RemoveUnpluggingHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_slotUnpluggingEventHandlers, slot, eventHandler);
        }

        internal void AddUnpluggedHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            AddHandler(_slotUnpluggedEventHandlers, slot, eventHandler);
        }

        internal void RemoveUnpluggedHandler(Slot slot, EventHandler<CompositionEventArgs> eventHandler)
        {
            RemoveHandler(_slotUnpluggedEventHandlers, slot, eventHandler);
        }

        internal void AddTaggingHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_slotTaggingEventHandlers, slot, eventHandler);
        }

        internal void RemoveTaggingHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_slotTaggingEventHandlers, slot, eventHandler);
        }

        internal void AddTaggedHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_slotTaggedEventHandlers, slot, eventHandler);
        }

        internal void RemoveTaggedHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_slotTaggedEventHandlers, slot, eventHandler);
        }

        internal void AddUntaggingHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_slotUntaggingEventHandlers, slot, eventHandler);
        }

        internal void RemoveUntaggingHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_slotUntaggingEventHandlers, slot, eventHandler);
        }

        internal void AddUntaggedHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            AddHandler(_slotUntaggedEventHandlers, slot, eventHandler);
        }

        internal void RemoveUntaggedHandler(Slot slot, EventHandler<TagEventArgs> eventHandler)
        {
            RemoveHandler(_slotUntaggedEventHandlers, slot, eventHandler);
        }

        internal void AddReflectionHandlers(Extension extension, object extensionObject)
        {
            var extensionType = extension.ExtensionType;

            InsertReflectionHandler(_extensionActivatedEventHandlers, extension,
                extensionType.OnActivated, extension, extensionObject);
            InsertReflectionHandler(_extensionDeactivatedEventHandlers,
                extension, extensionType.OnDeactivated, extension, extensionObject);
            
            foreach (var plug in extension.Plugs)
            {
                var plugType = plug.PlugType;

                InsertReflectionHandler(_plugPluggingEventHandlers, plug, plugType.OnPlugging, extension, extensionObject);
                InsertReflectionHandler(_plugPluggedEventHandlers, plug, plugType.OnPlugged, extension, extensionObject);
                InsertReflectionHandler(_plugUnpluggingEventHandlers, plug, plugType.OnUnplugging, extension, extensionObject);
                InsertReflectionHandler(_plugUnpluggedEventHandlers, plug, plugType.OnUnplugged, extension, extensionObject);
                InsertReflectionHandler(_plugTaggingEventHandlers, plug, plugType.OnTagging, extension, extensionObject);
                InsertReflectionHandler(_plugTaggedEventHandlers, plug, plugType.OnTagged, extension, extensionObject);
                InsertReflectionHandler(_plugUntaggingEventHandlers, plug, plugType.OnUntagging, extension, extensionObject);
                InsertReflectionHandler(_plugUntaggedEventHandlers, plug, plugType.OnUntagged, extension, extensionObject);
            }

            foreach (var slot in extension.Slots)
            {
                var slotType = slot.SlotType;

                InsertReflectionHandler(_slotOpeningEventHandlers, slot, slotType.OnOpening, extension, extensionObject);
                InsertReflectionHandler(_slotOpenedEventHandlers, slot, slotType.OnOpened, extension, extensionObject);
                InsertReflectionHandler(_slotClosingEventHandlers, slot, slotType.OnClosing, extension, extensionObject);
                InsertReflectionHandler(_slotClosedEventHandlers, slot, slotType.OnClosed, extension, extensionObject);
                InsertReflectionHandler(_slotPluggingEventHandlers, slot, slotType.OnPlugging, extension, extensionObject);
                InsertReflectionHandler(_slotPluggedEventHandlers, slot, slotType.OnPlugged, extension, extensionObject);
                InsertReflectionHandler(_slotUnpluggingEventHandlers, slot, slotType.OnUnplugging, extension, extensionObject);
                InsertReflectionHandler(_slotUnpluggedEventHandlers, slot, slotType.OnUnplugged, extension, extensionObject);
                InsertReflectionHandler(_slotTaggingEventHandlers, slot, slotType.OnTagging, extension, extensionObject);
                InsertReflectionHandler(_slotTaggedEventHandlers, slot, slotType.OnTagged, extension, extensionObject);
                InsertReflectionHandler(_slotUntaggingEventHandlers, slot, slotType.OnUntagging, extension, extensionObject);
                InsertReflectionHandler(_slotUntaggedEventHandlers, slot, slotType.OnUntagged, extension, extensionObject);
            }
        }

        internal void RemoveReflectionHandlers(Extension extension)
        {
            var extensionType = extension.ExtensionType;
            
            RemoveReflectionHandler(_extensionActivatedEventHandlers, extension, extensionType.OnActivated);
            RemoveReflectionHandler(_extensionDeactivatedEventHandlers, extension, extensionType.OnDeactivated);
            
            foreach (var plug in extension.Plugs)
            {
                var plugType = plug.PlugType;

                RemoveReflectionHandler(_plugPluggingEventHandlers, plug, plugType.OnPlugging);
                RemoveReflectionHandler(_plugPluggedEventHandlers, plug, plugType.OnPlugged);
                RemoveReflectionHandler(_plugUnpluggingEventHandlers, plug, plugType.OnUnplugging);
                RemoveReflectionHandler(_plugUnpluggedEventHandlers, plug, plugType.OnUnplugged);
                RemoveReflectionHandler(_plugTaggingEventHandlers, plug, plugType.OnTagging);
                RemoveReflectionHandler(_plugTaggedEventHandlers, plug, plugType.OnTagged);
                RemoveReflectionHandler(_plugUntaggingEventHandlers, plug, plugType.OnUntagging);
                RemoveReflectionHandler(_plugUntaggedEventHandlers, plug, plugType.OnUntagged);
            }

            foreach (var slot in extension.Slots)
            {
                var slotType = slot.SlotType;

                RemoveReflectionHandler(_slotOpeningEventHandlers, slot, slotType.OnOpening);
                RemoveReflectionHandler(_slotOpenedEventHandlers, slot, slotType.OnOpened);
                RemoveReflectionHandler(_slotClosingEventHandlers, slot, slotType.OnClosing);
                RemoveReflectionHandler(_slotClosedEventHandlers, slot, slotType.OnClosed);
                RemoveReflectionHandler(_slotPluggingEventHandlers, slot, slotType.OnPlugging);
                RemoveReflectionHandler(_slotPluggedEventHandlers, slot, slotType.OnPlugged);
                RemoveReflectionHandler(_slotUnpluggingEventHandlers, slot, slotType.OnUnplugging);
                RemoveReflectionHandler(_slotUnpluggedEventHandlers, slot, slotType.OnUnplugged);
                RemoveReflectionHandler(_slotTaggingEventHandlers, slot, slotType.OnTagging);
                RemoveReflectionHandler(_slotTaggedEventHandlers, slot, slotType.OnTagged);
                RemoveReflectionHandler(_slotUntaggingEventHandlers, slot, slotType.OnUntagging);
                RemoveReflectionHandler(_slotUntaggedEventHandlers, slot, slotType.OnUntagged);
            }
        }

        protected internal virtual void InvokeOnCompositionDone(Action handler)
        {
            InvokeOnCompositionDone(new ActionInvoker(handler));
        }

        protected internal virtual void InvokeOnCompositionDone<T>(Action<T> handler, T args)
        {
            InvokeOnCompositionDone(new ActionInvoker<T>(handler, args));
        }

        protected internal virtual void InvokeOnCompositionDone(Delegate @delegate, params object[] args)
        {
            InvokeOnCompositionDone(new DelegateInvoker(@delegate, args));
        }

        private void InvokeOnCompositionDone(IActionInvoker actionInvoker)
        {
            _notificationHook.OnHandlerAdded(actionInvoker);
            _onCompositionDoneStack.Push(actionInvoker);
            
            if (Composer.IsBusy)
                return;

            InvokeOnDoneHandlers();
        }

        internal BehaviorCollection GetSlotBehaviors(Slot slot)
        {
            if (!CheckAccess())
                throw GetAccessException();

            return
                _slotBehaviors.ContainsKey(slot)
                    ? _slotBehaviors[slot]
                    : (_slotBehaviors[slot] =
                       new SlotBehaviorCollection(_notificationHook, slot));
        }

        internal BehaviorCollection GetComposerBehaviors()
        {
            if (!CheckAccess())
                throw GetAccessException();
            
            return _composerBehaviors;
        }

        protected internal virtual void OnContractAdded(ContractEventArgs args)
        {
            Logger.LogContractAdded(args);
            _notificationHook.OnContractAdded(args);
            InvokeComposedHandlers(YieldEventHandlers(_typeStoreContractAddedEventHandlers), args);
        }

        protected internal virtual void OnContractRemoved(ContractEventArgs args)
        {
            Logger.LogContractRemoved(args);
            _notificationHook.OnContractRemoved(args);
            InvokeComposedHandlers(YieldEventHandlers(_typeStoreContractRemovedEventHandlers), args);
        }

        protected internal virtual void OnPluginAdded(PluginEventArgs args)
        {
            Logger.LogPluginAdded(args);
            _notificationHook.OnPluginAdded(args);
            InvokeComposedHandlers(YieldEventHandlers(_typeStorePluginAddedEventHandlers), args);
        }

        protected internal virtual void OnPluginRemoved(PluginEventArgs args)
        {
            Logger.LogPluginRemoved(args);
            _notificationHook.OnPluginRemoved(args);
            InvokeComposedHandlers(YieldEventHandlers(_typeStorePluginRemovedEventHandlers), args);
        }

        protected internal bool CanActivate(ExtensionEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(YieldCanActivateHandlers(args.Extension), args, out log);
        }

        protected internal bool CanOpen(SlotEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(GetCanOpenHandlers(args.Slot), args, out log);
        }

        protected internal bool CanPlug(CompositionEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(GetCanPlugHandlers(args.Slot), args, out log);
        }

        protected internal bool CanTag(TagEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(GetCanTagHandlers(args.Slot), args, out log);
        }

        protected internal bool CanDeactivate(ExtensionEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(GetCanDeactivateHandlers(args.Extension), args, out log);
        }

        protected internal bool CanClose(SlotEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(GetCanCloseHandlers(args.Slot), args, out log);
        }

        protected internal bool CanUnplug(CompositionEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(GetCanUnplugHandlers(args.Slot), args, out log);
        }

        protected internal bool CanUntag(TagEventArgs args, out LogArgs log)
        {
            return InvokeCanComposeHandlers(GetCanUntagHandlers(args.Tag.Slot), args, out log);
        }

        protected internal void OnActivating(ExtensionEventArgs args)
        {
            InvokeComposingHandlers(YieldActivatingHandlers(args.Extension), args);
        }

        protected internal void OnOpening(SlotEventArgs args)
        {
            InvokeComposingHandlers(YieldOpeningHandlers(args.Slot), args);
        }

        protected internal void OnPlugging(CompositionEventArgs args)
        {
            InvokeComposingHandlers(YieldPluggingHandlers(args.Slot, args.Plug), args);
        }

        protected internal void OnTagging(TagEventArgs args)
        {
            InvokeComposingHandlers(YieldTaggingHandlers(args.Tag.Slot, args.Tag.Plug), args);
        }

        protected internal void OnDeactivating(ExtensionEventArgs args)
        {
            InvokeComposingHandlers(YieldDeactivatingHandlers(args.Extension), args);
        }

        protected internal void OnClosing(SlotEventArgs args)
        {
            InvokeComposingHandlers(YieldClosingHandlers(args.Slot), args);
        }

        protected internal void OnUnplugging(CompositionEventArgs args)
        {
            InvokeComposingHandlers(YieldUnpluggingHandlers(args.Slot, args.Plug), args);
        }

        protected internal void OnUntagging(TagEventArgs args)
        {
            InvokeComposingHandlers(YieldUntaggingHandlers(args.Tag.Slot, args.Tag.Plug), args);
        }

        protected internal virtual void OnCreated(ExtensionEventArgs args)
        {
            Logger.LogCreated(args);
            _notificationHook.OnCreated(args);
            InvokeComposedHandlers(YieldCreatedHandlers(args.Extension.ExtensionType), args);
        }

        protected internal virtual void OnActivated(ExtensionEventArgs args)
        {
            Logger.LogActivated(args);
            _notificationHook.OnActivated(args);
            InvokeComposedHandlers(YieldActivatedHandlers(args.Extension), args);
        }

        protected internal virtual void OnOpened(SlotEventArgs args)
        {
            Logger.LogOpened(args);
            _notificationHook.OnOpened(args);
            InvokeComposedHandlers(YieldOpenedHandlers(args.Slot), args);
        }

        protected internal virtual void OnPlugged(CompositionEventArgs args)
        {
            Logger.LogPlugged(args);
            _notificationHook.OnPlugged(args);
            InvokeComposedHandlers(YieldPluggedHandlers(args.Slot, args.Plug), args);
        }

        protected internal virtual void OnTagged(TagEventArgs args)
        {
            Logger.LogTagged(args);
            _notificationHook.OnTagged(args);
            InvokeComposedHandlers(YieldTaggedHandlers(args.Tag.Slot, args.Tag.Plug), args);
        }

        protected internal virtual void OnDestroyed(ExtensionEventArgs args)
        {
            Logger.LogDestroyed(args);
            _notificationHook.OnDestroyed(args);
            InvokeComposedHandlers(YieldDestroyedHandlers(args.Extension.ExtensionType), args);
        }

        protected internal virtual void OnDeactivated(ExtensionEventArgs args)
        {
            Logger.LogDeactivated(args);
            _notificationHook.OnDeactivated(args);
            InvokeComposedHandlers(YieldDeactivatedHandlers(args.Extension), args);
        }

        protected internal virtual void OnClosed(SlotEventArgs args)
        {
            Logger.LogClosed(args);
            _notificationHook.OnClosed(args);
            InvokeComposedHandlers(YieldClosedHandlers(args.Slot), args);
        }

        protected internal virtual void OnUnplugged(CompositionEventArgs args)
        {
            Logger.LogUnplugged(args);
            _notificationHook.OnUnplugged(args);
            InvokeComposedHandlers(YieldUnpluggedHandlers(args.Slot, args.Plug), args);
        }

        protected internal virtual void OnUntagged(TagEventArgs args)
        {
            Logger.LogUntagged(args);
            _notificationHook.OnUntagged(args);
            InvokeComposedHandlers(YieldUntaggedHandlers(args.Tag.Slot, args.Tag.Plug), args);
        }

        protected virtual void OnUnhandledException(Exception e)
        {
            var args = new ExceptionEventArgs(e);

            Logger.LogUnhandledException(args);
            Runtime.OnUnhandledException(this, args);
        }

        private void AddHandler<TElem, TArgs>(Dictionary<TElem, EventHandlerWrapper<TArgs>[]> eventHandlerDictionary,
            TElem element, EventHandler<TArgs> eventHandler)
        {
            var eventHandlers =
                eventHandlerDictionary.ContainsKey(element)
                    ? eventHandlerDictionary[element]
                    : null;
            
            eventHandlerDictionary[element] = AddHandler(eventHandlers, eventHandler);
        }

        private void RemoveHandler<TElem, TArgs>(Dictionary<TElem, EventHandlerWrapper<TArgs>[]> eventHandlerDictionary,
            TElem element, EventHandler<TArgs> eventHandler)
        {
            if (!eventHandlerDictionary.ContainsKey(element))
                return;

            var eventHandlerWrapperArray =
                RemoveHandler(eventHandlerDictionary[element], eventHandler);

            if (eventHandlerWrapperArray == null)
                eventHandlerDictionary.Remove(element);
            else
                eventHandlerDictionary[element] = eventHandlerWrapperArray;
        }

        private void InsertReflectionHandler<TElem, TArgs>(Dictionary<TElem, EventHandlerWrapper<TArgs>[]> eventHandlerDictionary,
            TElem element, string eventHandlerName, Extension extension, object target)
        {
            if (string.IsNullOrEmpty(eventHandlerName))
                return;

            var method =
                target.GetType().GetMethod(
                    eventHandlerName,
                    BindingFlags.Instance |
                    BindingFlags.Static |
                    BindingFlags.Public |
                    BindingFlags.NonPublic,
                    null,
                    new[] {typeof (TArgs)},
                    null);

            if (method == null)
                return;

            var eventHandlers =
                eventHandlerDictionary.ContainsKey(element)
                    ? eventHandlerDictionary[element]
                    : null;

            EventHandler<TArgs> eventHandler =
                new ExtensionEventHandler<TArgs>(Invoker, extension, target, method).Invoke;

            eventHandlerDictionary[element] = InsertHandler(eventHandlers, 0, eventHandler);
        }

        private void RemoveReflectionHandler<TElem, TArgs>(Dictionary<TElem, EventHandlerWrapper<TArgs>[]> eventHandlerDictionary,
            TElem element, string eventHandlerName)
        {
            if (string.IsNullOrEmpty(eventHandlerName) || !eventHandlerDictionary.ContainsKey(element))
                return;

            var eventHandler = eventHandlerDictionary[element][0].EventHandler;
            
            if (!(eventHandler.Target is ExtensionEventHandler<TArgs>))
                return;

            RemoveHandler(eventHandlerDictionary, element, eventHandler);
        }

        private EventHandlerWrapper<TArgs>[] AddHandler<TArgs>(EventHandlerWrapper<TArgs>[] eventHandlers,
            EventHandler<TArgs> eventHandler)
        {
            var element = new EventHandlerWrapper<TArgs>(eventHandler);
            
            _notificationHook.OnHandlerAdded(element);
            
            return ArrayEx.Add(eventHandlers, element);
        }

        private EventHandlerWrapper<TArgs>[] InsertHandler<TArgs>(EventHandlerWrapper<TArgs>[] eventHandlers,
            int index, EventHandler<TArgs> eventHandler)
        {
            var element = new EventHandlerWrapper<TArgs>(eventHandler);
            
            _notificationHook.OnHandlerAdded(element);
            
            return ArrayEx.Insert(eventHandlers, index, element);
        }

        private EventHandlerWrapper<TArgs>[] RemoveHandler<TArgs>(EventHandlerWrapper<TArgs>[] eventHandlers,
            EventHandler<TArgs> eventHandler)
        {
            var element = eventHandlers.FirstOrDefault(
                eventHandlerWrapper => eventHandlerWrapper.EventHandler.Equals(eventHandler));

            if (element == null)
                return eventHandlers;

            _notificationHook.OnHandlerRemoved(element);
            
            return ArrayEx.Remove(eventHandlers, element);
        }

        private bool InvokeCanComposeHandlers<T>(IEnumerable<CanHandler<T>> canHandlers, T args, out LogArgs log)
        {
            var executingCanHandler = IsExecutingCanHandler;
            IsExecutingCanHandler = false;

            var invoked = true;
            log = null;
            
            foreach (var canHandler in canHandlers)
            {
                try
                {
                    LogArgs logArgs;

                    if (canHandler(args, out logArgs))
                        continue;

                    log = logArgs;
                    invoked = false;
                    break;
                }
                catch (Exception ex)
                {
                    OnUnhandledException(ex);
                }
            }
            
            IsExecutingCanHandler = executingCanHandler;

            return invoked;
        }

        private void InvokeComposingHandlers<T>(IEnumerable<Action<T>> eventHandlers, T args)
        {
            if (eventHandlers == null)
                return;
            
            foreach (var action in eventHandlers)
                Invoke(new ActionInvoker<T>(action, args));
        }

        private void InvokeComposedHandlers<T>(IEnumerable<Action<T>> eventHandlers, T args)
        {
            if (eventHandlers == null)
                return;

            foreach (var action in eventHandlers)
                Invoke(new ActionInvoker<T>(action, args));
        }

        internal void Continue()
        { }

        private void Invoke(IActionInvoker action)
        {
            _notificationHook.OnHandlerInvocationStarted(action.Target);

            try
            {
                action.Invoke();
            }
            catch (Exception ex)
            {
                OnUnhandledException(ex);
            }
            finally
            {
                _notificationHook.OnHandlerInvocationFinished(action.Target);
            }
        }

        internal void InvokeOnDoneHandlers()
        {
            if (_isOnDoneHandlerExecutionInvoked)
                return;
            
            _isOnDoneHandlerExecutionInvoked = true;
            _executeOnDoneHandlers();
        }

        private void ExecuteOnDoneHandlers()
        {
            _isOnDoneHandlerExecutionInvoked = false;
            TypeStoreModifier.OnCompositionDone();
            
            while (_onCompositionDoneStack.Count > 0)
            {
                var actionInvoker = _onCompositionDoneStack.Pop();
                _notificationHook.OnHandlerInvocationStarted(actionInvoker);
                
                try
                {
                    actionInvoker.Invoke();
                }
                catch (Exception ex)
                {
                    Runtime.OnUnhandledException(this, new ExceptionEventArgs(ex));
                }
                finally
                {
                    _notificationHook.OnHandlerInvocationFinished(actionInvoker);
                }

                _notificationHook.OnHandlerRemoved(actionInvoker);
            }

            ++_onDoneHandlerExcutionId;
            var executionId = _onDoneHandlerExcutionId;

            if (_composerCompositionDoneEventHandlers == null)
                return;

            foreach (var eventHandlerWrapper in _composerCompositionDoneEventHandlers)
            {
                _notificationHook.OnHandlerInvocationStarted(eventHandlerWrapper);

                try
                {
                    eventHandlerWrapper.Invoke(EventArgs.Empty);
                }
                finally
                {
                    _notificationHook.OnHandlerInvocationFinished(eventHandlerWrapper);
                }

                if (executionId != _onDoneHandlerExcutionId)
                    return;
            }
            
            _onDoneHandlerExcutionId = 0;
        }

        private IEnumerable<Action<T>> YieldEventHandlers<T>(EventHandlerWrapper<T>[] eventHandlerWrappers)
        {
            if (eventHandlerWrappers == null) yield break;

            foreach (var eventHandlerWrapper in eventHandlerWrappers)
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<CanHandler<ExtensionEventArgs>> YieldCanActivateHandlers(Extension extension)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanActivate;

            foreach (var behavior in
                from plug in extension.Plugs
                from key in plug.SlotsWherePlugged
                where _slotBehaviors.ContainsKey(key)
                from behavior in _slotBehaviors[key]
                select behavior)
                yield return behavior.CanActivate;
        }

        private IEnumerable<CanHandler<SlotEventArgs>> GetCanOpenHandlers(Slot slot)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanOpen;

            if (!_slotBehaviors.ContainsKey(slot))
                yield break;

            foreach (var behavior in _slotBehaviors[slot])
                yield return behavior.CanOpen;
        }

        private IEnumerable<CanHandler<CompositionEventArgs>> GetCanPlugHandlers(Slot slot)
        {
            yield return (CompositionEventArgs args,
                out LogArgs log) => _permissionHook.CanPlug(args.Slot, args.Plug, out log);

            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanPlug;

            if (!_slotBehaviors.ContainsKey(slot))
                yield break;

            foreach (var behavior in _slotBehaviors[slot])
                yield return behavior.CanPlug;
        }

        private IEnumerable<CanHandler<TagEventArgs>> GetCanTagHandlers(Slot slot)
        {
            yield return (TagEventArgs args,
                out LogArgs log) => _permissionHook.CanTag(args.Slot, args.Plug, args.Name, out log);
            
            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanTag;

            if (!_slotBehaviors.ContainsKey(slot))
                yield break;

            foreach (var behavior in _slotBehaviors[slot])
                yield return behavior.CanTag;
        }

        private IEnumerable<CanHandler<ExtensionEventArgs>> GetCanDeactivateHandlers(Extension extension)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanDeactivate;

            foreach (var behavior in
                from plug in extension.Plugs
                from key in plug.SlotsWherePlugged
                where _slotBehaviors.ContainsKey(key)
                from behavior in _slotBehaviors[key]
                select behavior)
                yield return behavior.CanDeactivate;
        }

        private IEnumerable<CanHandler<SlotEventArgs>> GetCanCloseHandlers(Slot slot)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanClose;

            if (!_slotBehaviors.ContainsKey(slot))
                yield break;

            foreach (var behavior in _slotBehaviors[slot])
                yield return behavior.CanClose;
        }

        private IEnumerable<CanHandler<CompositionEventArgs>> GetCanUnplugHandlers(Slot slot)
        {
            yield return (CompositionEventArgs args,
                out LogArgs log) => _permissionHook.CanUnplug(args.Slot, args.Plug, out log);
            
            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanUnplug;

            if (!_slotBehaviors.ContainsKey(slot))
                yield break;

            foreach (var behavior in _slotBehaviors[slot])
                yield return behavior.CanUnplug;
        }

        private IEnumerable<CanHandler<TagEventArgs>> GetCanUntagHandlers(Slot slot)
        {
            yield return (TagEventArgs args,
                out LogArgs log) => _permissionHook.CanUntag(args.Slot, args.Plug, args.Tag.Name, out log);
            
            foreach (var behavior in _composerBehaviors)
                yield return behavior.CanUntag;

            if (!_slotBehaviors.ContainsKey(slot))
                yield break;

            foreach (var behavior in _slotBehaviors[slot])
                yield return behavior.CanUntag;
        }

        private IEnumerable<Action<ExtensionEventArgs>> YieldActivatingHandlers(Extension extension)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Activating;

            foreach (var behavior in
                from plug in extension.Plugs
                from key in plug.slotsWherePlugged
                where _slotBehaviors.ContainsKey(key)
                from behavior in _slotBehaviors[key]
                select behavior)
                yield return behavior.Activating;
        }

        private IEnumerable<Action<SlotEventArgs>> YieldOpeningHandlers(Slot slot)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Opening;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Opening;

            if (!_slotOpeningEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotOpeningEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<CompositionEventArgs>> YieldPluggingHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Plugging;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Plugging;

            if (_slotPluggingEventHandlers.ContainsKey(slot))
                foreach (var eventHandlerWrapper in _slotPluggingEventHandlers[slot])
                    yield return eventHandlerWrapper.Invoke;

            if (!_plugPluggingEventHandlers.ContainsKey(plug)) yield break;
            foreach (var eventHandlerWrapper in _plugPluggingEventHandlers[plug])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<TagEventArgs>> YieldTaggingHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Tagging;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Tagging;

            if (_slotTaggingEventHandlers.ContainsKey(slot))
                foreach (var eventHandlerWrapper in _slotTaggingEventHandlers[slot])
                    yield return eventHandlerWrapper.Invoke;

            if (!_plugTaggingEventHandlers.ContainsKey(plug))
                yield break;

            foreach (var eventHandlerWrapper in _plugTaggingEventHandlers[plug])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<ExtensionEventArgs>> YieldDeactivatingHandlers(Extension extension)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Deactivating;

            foreach (var behavior in
                from plug in extension.Plugs
                from key in plug.SlotsWherePlugged
                where _slotBehaviors.ContainsKey(key)
                from behavior in _slotBehaviors[key]
                select behavior)
                yield return behavior.Deactivating;
        }

        private IEnumerable<Action<SlotEventArgs>> YieldClosingHandlers(Slot slot)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Closing;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Closing;

            if (!_slotClosingEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotClosingEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<CompositionEventArgs>> YieldUnpluggingHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Unplugging;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Unplugging;

            if (_plugUnpluggingEventHandlers.ContainsKey(plug))
                foreach (var eventHandlerWrapper in _plugUnpluggingEventHandlers[plug])
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotUnpluggingEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotUnpluggingEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<TagEventArgs>> YieldUntaggingHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Untagging;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Untagging;

            if (_plugUntaggingEventHandlers.ContainsKey(plug))
                foreach (var eventHandlerWrapper in _plugUntaggingEventHandlers[plug])
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotUntaggingEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotUntaggingEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<ExtensionEventArgs>> YieldCreatedHandlers(ExtensionType extensionType)
        {
            if (_composerExtensionCreatedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionCreatedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (!_extensionTypeCreatedEventHandlers.ContainsKey(extensionType))
                yield break;

            foreach (var eventHandlerWrapper in _extensionTypeCreatedEventHandlers[extensionType])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<ExtensionEventArgs>> YieldActivatedHandlers(Extension extension)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Activated;

            foreach (var behavior in
                from plug in extension.Plugs
                from key in plug.SlotsWherePlugged
                where _slotBehaviors.ContainsKey(key)
                from behavior in _slotBehaviors[key]
                select behavior)
                yield return behavior.Activated;

            if (_composerExtensionActivatedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionActivatedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (!_extensionActivatedEventHandlers.ContainsKey(extension) ||
                _extensionActivatedEventHandlers[extension] == null)
                yield break;

            foreach (var eventHandlerWrapper in _extensionActivatedEventHandlers[extension])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<SlotEventArgs>> YieldOpenedHandlers(Slot slot)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Opened;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Opened;

            if (_composerSlotOpenedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerSlotOpenedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotOpenedEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotOpenedEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<CompositionEventArgs>> YieldPluggedHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Plugged;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Plugged;

            if (_composerExtensionPluggedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionPluggedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (_plugPluggedEventHandlers.ContainsKey(plug))
                foreach (var eventHandlerWrapper in _plugPluggedEventHandlers[plug])
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotPluggedEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotPluggedEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<TagEventArgs>> YieldTaggedHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Tagged;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Tagged;

            if (_composerExtensionTaggedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionTaggedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (_plugTaggedEventHandlers.ContainsKey(plug))
                foreach (var eventHandlerWrapper in _plugTaggedEventHandlers[plug])
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotTaggedEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotTaggedEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<ExtensionEventArgs>> YieldDestroyedHandlers(ExtensionType extensionType)
        {
            if (_composerExtensionDestroyedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionDestroyedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (!_extensionTypeDestroyedEventHandlers.ContainsKey(extensionType))
                yield break;

            foreach (var eventHandlerWrapper in _extensionTypeDestroyedEventHandlers[extensionType])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<ExtensionEventArgs>> YieldDeactivatedHandlers(Extension extension)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Deactivated;

            foreach (var behavior in
                from plug in extension.Plugs
                from key in plug.SlotsWherePlugged
                where _slotBehaviors.ContainsKey(key)
                from behavior in _slotBehaviors[key]
                select behavior)
                yield return behavior.Deactivated;

            if (_composerExtensionDeactivatedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionDeactivatedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (!_extensionDeactivatedEventHandlers.ContainsKey(extension))
                yield break;

            foreach (var eventHandlerWrapper in _extensionDeactivatedEventHandlers[extension])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<SlotEventArgs>> YieldClosedHandlers(Slot slot)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Closed;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Closed;

            if (_composerSlotClosedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerSlotClosedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotClosedEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotClosedEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<CompositionEventArgs>> YieldUnpluggedHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Unplugged;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Unplugged;

            if (_composerExtensionUnpluggedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionUnpluggedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (_plugUnpluggedEventHandlers.ContainsKey(plug))
                foreach (var eventHandlerWrapper in _plugUnpluggedEventHandlers[plug])
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotUnpluggedEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotUnpluggedEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }

        private IEnumerable<Action<TagEventArgs>> YieldUntaggedHandlers(Slot slot, Plug plug)
        {
            foreach (var behavior in _composerBehaviors)
                yield return behavior.Untagged;

            if (_slotBehaviors.ContainsKey(slot))
                foreach (var behavior in _slotBehaviors[slot])
                    yield return behavior.Untagged;

            if (_composerExtensionUntaggedEventHandlers != null)
                foreach (var eventHandlerWrapper in _composerExtensionUntaggedEventHandlers)
                    yield return eventHandlerWrapper.Invoke;

            if (_plugUntaggedEventHandlers.ContainsKey(plug))
                foreach (var eventHandlerWrapper in _plugUntaggedEventHandlers[plug])
                    yield return eventHandlerWrapper.Invoke;

            if (!_slotUntaggedEventHandlers.ContainsKey(slot))
                yield break;

            foreach (var eventHandlerWrapper in _slotUntaggedEventHandlers[slot])
                yield return eventHandlerWrapper.Invoke;
        }
    }
}
